package org.misty.practices.inject.core;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

public class AppContext {
    private ConcurrentHashMap<Class<?>, Object> cache = new ConcurrentHashMap<>();

    public void register(Class<?> cls) {
        if (cls == null) return;
        try {
            var interfaces = cls.getInterfaces();
            if (interfaces.length == 0) {
                throw new RuntimeException("注册的类必须实现一个接口");
            }
            for (var iface : interfaces) {
                if (cache.containsKey(iface)) {
                    throw new RuntimeException("接口重复：" + cls);
                }
            }

            var bean = cls.getConstructor().newInstance();

            var fields = new ArrayList<Field>();
            var clsTemp = cls;
            do {
                // 获取所有属性
                // 如果要支持setter注入，可以考虑 Introspector.getBeanInfo()
                fields.addAll(Arrays.asList(cls.getDeclaredFields()));
                clsTemp = clsTemp.getSuperclass();
            } while (clsTemp != null && clsTemp != Object.class);
            for (Field field : fields) {
                // 只处理有Autowired标注的属性（只支持接口注入）
                if (isInjectable(field) && field.getType().isInterface()) {
                    field.setAccessible(true);
                    var type = field.getType();
                    if (cache.containsKey(type)) {
                        // 如果该接口已经注册过，直接注入
                        field.set(bean, cache.get(type));
                    } else {
                        // 否则注入一个代理对象（如要支持普通类注入需要Cglib）
                        field.set(bean, lazyLoadProxy(type, bean, field));
                    }
                }
            }

            for (var iface : interfaces) {
                cache.put(iface, bean);
            }
            cache.put(cls, bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<?> cls) {
        return (T) cache.get(cls);
    }

    private Object lazyLoadProxy(Class<?> iface, final Object bean, final Field field) {
        return Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, (proxy, method, args) -> {
//            System.out.println(method);
            // 代理对象的方法被调用时，用真正的依赖对象替换掉代理对象
            var instance = cache.get(iface);
            field.set(bean, instance);
            // 别忘了调用method，否则这次方法调用会被吞掉
            return method.invoke(instance, args);
        });
    }

    private static boolean isInjectable(Field field) {
        return field.isAnnotationPresent(Autowired.class);
    }
}
